package utils

import (
	"bytes"
	"context"
	"encoding/gob"
	"encoding/json"
	"gitee.com/zxs-micro/zxs-micro-business/protos/businesses"
	grpc2 "gitee.com/zxs-micro/zxs-micro-common/grpc"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/clients"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/common/model"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"sync"
)

type CommonBussClient struct {
	c businesses.BusinessesClient
	clients.CommonClients
}

var cs = make(map[string]*CommonBussClient)
var clock sync.Mutex

func GenerateBussClients(addr string) (*CommonBussClient, error) {
	var err error
	clock.Lock()
	cli, ok := cs[addr]
	clock.Unlock()
	if !ok {
		cli = new(CommonBussClient)
		cli.ClientConn, err = grpc.Dial(addr, grpc2.GetClientDialOpts()...)
		if err != nil {
			return nil, err
		}
		cli.c = businesses.NewBusinessesClient(cli.ClientConn)
		cli.RemoteAddr = addr
		clock.Lock()
		cs[addr] = cli
		clock.Unlock()
	}
	return cli, nil
}

func (c *CommonBussClient) ExecBusiness2Bus(params [][]byte, token, url string) ([]byte, error) {
	err := c.CheckConn()
	if err != nil {
		return nil, err
	}
	result, err := c.c.Exec(context.Background(), &businesses.ExecRequest{
		Params:  params,
		Token:   token,
		ExecUrl: url,
	})
	if err != nil {
		return nil, err
	}
	return result.Payload, err
}

func (c *CommonBussClient) CheckConn() (err error) {
	c.ClientLock.Lock()
	if c.ClientConn.GetState() == connectivity.Shutdown {
		c.ClientConn, err = grpc.Dial(c.RemoteAddr, grpc2.GetClientDialOpts()...)
		c.c = businesses.NewBusinessesClient(c.ClientConn)
	}
	c.ClientLock.Unlock()
	return
}

func (c *CommonBussClient) ShutdownClient() {
	c.ClientLock.Lock()
	c.ClientConn.Close()
	c.ClientLock.Unlock()
	clock.Lock()
	delete(cs, c.RemoteAddr)
	clock.Unlock()
}

func GenerateExecParamBytes(t, url string, params [][]byte) []byte {
	ep := new(model.ExecParams)
	ep.Token = t
	ep.Execurl = url
	ep.ExecParams = params
	bs, _ := json.Marshal(ep)
	return bs
}

func GenerateExecParams(url, token string, datas ...interface{}) (*model.ExecParams, error) {
	ep := new(model.ExecParams)
	ep.Execurl = url
	ep.Token = token
	bss := make([][]byte, 0, 0)
	for _, o := range datas {
		bs, err := GetBytes(o)
		if err != nil {
			return nil, err
		}
		bss = append(bss, bs)
	}
	ep.ExecParams = bss
	return ep, nil
}

func GetBytes(key interface{}) ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(key)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func GetIntf(data []byte, to interface{}) error {
	buf := bytes.NewBuffer(data)
	dec := gob.NewDecoder(buf)
	return dec.Decode(to)
}
